package org.deng.learn.concurrent.pc;

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author 邓力宾
 * @date 2020/5/15 下午4:30
 * Condition的await和signal等待/通知机制: https://www.jianshu.com/p/28387056eeb4
 * Condition:
 * ConditionObject(Condition的实现类)是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要
 * 获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队
 * 列(以下称为等待队列),该队列是Condition对象实现等待/通知功能的关键。
 * 下面将分析Condition的实现,主要包括:等待队列、等待和通知,下面提到的Condition如
 * 果不加说明均指的是ConditionObject。
 * 1.等待队列
 * 等待队列是一个FIFO的队列,在队列中的每个节点都包含了一个线程引用,该线程就是
 * 在Condition对象上等待的线程,如果一个线程调用了Condition.await()方法,那么该线程将会
 * 释放锁、构造成节点加入等待队列并进入等待状态。事实上,节点的定义复用了同步器中节点
 * 的定义,也就是说,同步队列和等待队列中节点类型都是同步器的静态内部类
 * AbstractQueuedSynchronizer.Node。
 * 一个Condition包含一个等待队列,Condition拥有首节点(firstWaiter)和尾节点
 * (lastWaiter)。当前线程调用Condition.await()方法,将会以当前线程构造节点,并将节点从尾部
 * 加入等待队列
 * @desc 使用lock condition实现生产者消费者模式
 */
public class LockConditionProducerConsumerDemo<T> {

    //容器
    final  private LinkedList<T> list = new LinkedList<>();
    final private int max = 5; //最大存放元素个数
    private  int count = 0; //当前元素个数
    private Lock lock = new ReentrantLock();
    private Condition producer = lock.newCondition(); // 实际返回的是ConditionObject对象
    private Condition consumer = lock.newCondition();
    //生产
    public void put(T t){
        try{
            lock.lock(); //上锁
            while (count == max){
                System.out.println("满了");
                //等着 当前线程加入等待队列，释放当前线程所占用的lock，在释放的过程中会唤醒同步队列中的下一个节点，阻塞当前线程
                //当被唤醒时,会自旋等待获取到同步状态（即获取到lock）
                producer.await();
            }
            list.add(t);
            count++;
            System.out.println("生产了一个元素"+t);
            consumer.signalAll();//通知消费者消费
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("生产者释放锁");
            lock.unlock();//释放锁
        }
    }
    //消费
    public  T get(){
        T t = null;
        try{
            lock.lock();
            while (count == 0){
                consumer.await();//等着 线程调用了Condition.await()方法,那么该线程将会 释放锁、构造成节点加入等待队列并进入等待状态
            }
            t = list.removeFirst();
            count--;
            System.out.println("消费了一个元素"+t);
            producer.signalAll();//通知生产者生产
            return t;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("消费者释放锁");
            lock.unlock();
        }
        return t;
    }
    public  static  void main(String[] args) throws InterruptedException {
        LockConditionProducerConsumerDemo<String> producerConsumer2 = new LockConditionProducerConsumerDemo<>();
        new Thread(()->{
            for(int i = 0; i < 10; i++){
                producerConsumer2.put(i+"");
            }
        }).start();
        new Thread(()->{
            for(int i = 0; i < 10; i++){
                producerConsumer2.get();
            }
        }).start();
    }
}
